package cn.toolck.filter;

import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;

import java.util.*;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

@Activate(group = {CommonConstants.CONSUMER})
public class TPMonitorFilter implements Filter {
    private Map<String, Deque<List<Long>>> treeMap = new HashMap<>();
    private ReentrantLock lock = new ReentrantLock();
    private final int TOTAL_SECONDS = 60;
    private final int SPLIT_SECONDS = 5;
    private final double TP90 = 0.9;
    private final double TP99 = 0.99;

    public TPMonitorFilter() {
        // 5秒钟统计一次
        Executors.newSingleThreadScheduledExecutor().scheduleWithFixedDelay(() -> {
            try {
                Map<String, String> run90 = run(TP90);
                Map<String, String> run99 = run(TP99);
                remove();
                System.out.println("run90:" + run90);
                System.out.println("run99:" + run99);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, 0, SPLIT_SECONDS, TimeUnit.SECONDS);
    }

    private void remove() {
        treeMap.forEach((key, val) -> {
            if (val.size() > TOTAL_SECONDS / SPLIT_SECONDS) {
                val.removeFirst();
            }
            val.addLast(new ArrayList<>());
        });
    }

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        URL url = invoker.getUrl();
        long start = System.currentTimeMillis();
        Result invoke = invoker.invoke(invocation);
        long end = System.currentTimeMillis();
        long time = end - start;
//        System.out.println("使用了：" + time + "ms url:" + url);
        addURL(invocation, time);
        return invoke;
    }

    private synchronized void addURL(Invocation invocation, long time) {
        String serviceName = invocation.getServiceName();
        String methodName = invocation.getMethodName();
        String name = serviceName + ":" + methodName;
        Deque<List<Long>> lists = treeMap.get(name);
        if (lists == null) {
            lists = new ConcurrentLinkedDeque<List<Long>>();
            lists.add(new ArrayList<>());
            treeMap.put(name, lists);
        }

        if (lists.isEmpty()) {
            lists.add(new ArrayList<>());
        }
        lists.getLast().add(time);
    }

    private Map<String, String> run(double i) {
        Map<String, String> map = new HashMap<>();
        treeMap.forEach((key, val) -> {
            ArrayList<Long> total = new ArrayList<>();
            System.out.println("==========================================");
            val.forEach(v -> {
                System.out.println(v.size());
            });
            System.out.println("==========================================");
            val.forEach(total::addAll);
            int maxSize = (int) Math.ceil(i * total.size());
            Optional<Long> min = total.stream().sorted().limit(maxSize).max(Comparator.comparingLong(Long::longValue));
            map.put(key, min.get() + " t: " + total.size());
        });
        return map;
    }
}
